package dk.aau.medialogy.y2013.p4;

import java.util.ArrayList;
import java.util.List;

import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PImage;
import controlP5.ControlFont;
import controlP5.ControlP5;
import ddf.minim.Minim;
import ddf.minim.analysis.FFT;
import ddf.minim.effects.LowPassSP;
import ddf.minim.ugens.Multiplier;
import ddf.minim.ugens.UGen;

import java.util.List;
import java.util.ArrayList;

public class Main extends PApplet
{
	//Set serial version
	private static final long serialVersionUID = 1L;

	//Declare an Oscillator from the other package. Whole path is used as minim also has a class called Oscillator. and declare a variable to hold the Oscillators frequency
	public static dk.aau.medialogy.y2013.p4.Oscillator osc;
	public static dk.aau.medialogy.y2013.p4.Oscillator flangerOsc;
	float oscilFreq = 0;
	
	//Declare variables to see if frequency of Oscillator has been changed or effects has been added or removed
	boolean freqChanged = false;
	boolean effectsChanged = false;
	
	//Declare list for containing all effects
	List<UGen> effectsList;

	//Declare variable to hold the amount of effects currently in the list
	int lastSize = 0;
	
	//Declare starter which is a multiplier used in the effectslist.
	Multiplier starter;
	
	//Declare string used to switch from sinusoidal wave to saw wave
	String waveType = "saw";
	
	//Toogle music on/off
	boolean playSong = true;
	
	//Togle mic on/off
	boolean UseMic = false;
	
	//Togle for showing and hideing domains
	boolean showVisual = true;
	
	public void resetAll()
	{
		SystemManager.cp5.getController("Chorus1").setValue(0);
		SystemManager.cp5.getController("Chorus2").setValue(0);
		SystemManager.cp5.getController("Chorus3").setValue(0);
		SystemManager.cp5.getController("Chorus4").setValue(0);
		SystemManager.cp5.getController("distortion1").setValue(0);
		SystemManager.cp5.getController("distortion2").setValue(0);
		SystemManager.cp5.getController("distortion3").setValue(0);
		SystemManager.cp5.getController("echo1").setValue(0);
		SystemManager.cp5.getController("echo2").setValue(0);
		SystemManager.cp5.getController("echo3").setValue(0);
		SystemManager.cp5.getController("echo4").setValue(0);
		SystemManager.cp5.getController("equilizer1").setValue(0);
		SystemManager.cp5.getController("equilizer2").setValue(0);
		SystemManager.cp5.getController("equilizer3").setValue(0);
		SystemManager.cp5.getController("equilizer4").setValue(0);
		SystemManager.cp5.getController("phasor1").setValue(0);
		SystemManager.cp5.getController("phasor2").setValue(0);
		SystemManager.cp5.getController("phasor3").setValue(0);
		SystemManager.cp5.getController("phasor4").setValue(0);
		SystemManager.cp5.getController("phasor5").setValue(0);
		SystemManager.cp5.getController("tremolo1").setValue(0);
		SystemManager.cp5.getController("vibrato1").setValue(0);
		SystemManager.cp5.getController("wahwah1").setValue(0);
		SystemManager.cp5.getController("wahwah2").setValue(0);
		SystemManager.cp5.getController("wahwah3").setValue(0);
	}
	
	//Function to hide all controls which are used for the different effects
	public void hideAll()
	{
		SystemManager.cp5.getController("chorus_Button").setImage(img1);
		SystemManager.cp5.getController("distortion_Button").setImage(img2);
		SystemManager.cp5.getController("echo_Button").setImage(img3);
		SystemManager.cp5.getController("equilizer_Button").setImage(img4);
		SystemManager.cp5.getController("phasor_Button").setImage(img5);
		SystemManager.cp5.getController("tremolo_Button").setImage(img6);
		SystemManager.cp5.getController("vibrato_Button").setImage(img7);
		SystemManager.cp5.getController("wahwah_Button").setImage(img8);
		
		SystemManager.cp5.getController("Chorus1").hide();
		SystemManager.cp5.getController("Chorus2").hide();
		SystemManager.cp5.getController("Chorus3").hide();
		SystemManager.cp5.getController("Chorus4").hide();
		SystemManager.cp5.getController("distortion1").hide();
		SystemManager.cp5.getController("distortion2").hide();
		SystemManager.cp5.getController("distortion3").hide();
		SystemManager.cp5.getController("echo1").hide();
		SystemManager.cp5.getController("echo2").hide();
		SystemManager.cp5.getController("echo3").hide();
		SystemManager.cp5.getController("echo4").hide();
		SystemManager.cp5.getController("equilizer1").hide();
		SystemManager.cp5.getController("equilizer2").hide();
		SystemManager.cp5.getController("equilizer3").hide();
		SystemManager.cp5.getController("equilizer4").hide();
		SystemManager.cp5.getController("phasor1").hide();
		SystemManager.cp5.getController("phasor2").hide();
		SystemManager.cp5.getController("phasor3").hide();
		SystemManager.cp5.getController("phasor4").hide();
		SystemManager.cp5.getController("phasor5").hide();
		SystemManager.cp5.getController("tremolo1").hide();
		SystemManager.cp5.getController("vibrato1").hide();
		SystemManager.cp5.getController("wahwah1").hide();
		SystemManager.cp5.getController("wahwah2").hide();
		SystemManager.cp5.getController("wahwah3").hide();
		
		SystemManager.cp5.getController("choruslabel").hide();
		SystemManager.cp5.getController("distortionlabel").hide();
		SystemManager.cp5.getController("echolabel").hide();
		SystemManager.cp5.getController("equilizerlabel").hide();
		SystemManager.cp5.getController("phasorlabel").hide();
		SystemManager.cp5.getController("vibratolabel").hide();
		SystemManager.cp5.getController("wahwahlabel").hide();
		SystemManager.cp5.getController("tremololabel").hide();
		
		//resetAll();
		
	}

	//Declare and initialize integer holding a width given from systemManager
	int[] quant = new int[SystemManager.WINDOW_WIDTH];
	
	//Load images for the effectbuttons
	PImage img1 = loadImage("symbol_chorus.png");
	PImage img2 = loadImage("symbol_distortion.png");
	PImage img3 = loadImage("symbol_echo.png");
	PImage img4 = loadImage("symbol_equilizer.png");
	PImage img5 = loadImage("symbol_phaser.png");
	PImage img6 = loadImage("symbol_tremolo.png");
	PImage img7 = loadImage("symbol_vibrato.png");
	PImage img8 = loadImage("symbol_wahwah.png");
	PImage img1d = loadImage("symbol_chorus_dark.png");
	PImage img2d = loadImage("symbol_distortion_dark.png");
	PImage img3d = loadImage("symbol_echo_dark.png");
	PImage img4d = loadImage("symbol_equilizer_dark.png");
	PImage img5d = loadImage("symbol_phaser_dark.png");
	PImage img6d = loadImage("symbol_tremolo_dark.png");
	PImage img7d = loadImage("symbol_vibrato_dark.png");
	PImage img8d = loadImage("symbol_wahwah_dark.png");
	
	//Create array for the pictures and put them in
	PImage imgs[] =
	{ img1, img2, img3, img4, img5, img6, img7, img8 ,img1d ,img2d ,img3d ,img4d ,img5d ,img6d ,img7d ,img8d};
	
	//Font for the program
	PFont pfont = createFont("Arial",10,true); // use true/false for smooth/no-smooth
	 ControlFont headerFont = new ControlFont(pfont,50);

	//Function which is run once and then never again
	public void setup()
	{
		
		// setup window size
		size(SystemManager.WIDTH, SystemManager.HEIGHT);

		// setup minim
		SystemManager.minim = new Minim(this);

		// setup GUI
		SystemManager.cp5 = new ControlP5(this);
		
		//Resize images so that they actually fit and work
		img1.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img1d.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img2.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img3.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img4.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img5.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img6.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img7.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img8.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img2d.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img3d.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img4d.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img5d.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img6d.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img7d.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		img8d.resize(SystemManager.WIDTH - SystemManager.ADDED_BAR, 0);
		
		//call function from Button class
		Button.Setup(imgs, headerFont);
		
		//SystemManager.in is set to the linein, which is the microphone on most computers
		SystemManager.in = SystemManager.minim.getLineIn(Minim.STEREO, SystemManager.NEXT_POW);
		
		//SystemManger.out set to the lineout, which is the speakers
		SystemManager.out = SystemManager.minim.getLineOut(Minim.STEREO,
				SystemManager.NEXT_POW);
		
		//The song to be played is loaded
		SystemManager.song = SystemManager.minim.loadFile(
				"C:/tremolo.wav", SystemManager.NEXT_POW);
		
		//Check if playSong is true, if it is set the song to be played
		if (playSong)
		{
			SystemManager.song.loop();
		}
		
		
		// Setup to run FFT on the song, systemManager.in and what goes through systemManager.out
		SystemManager.fftSong = new FFT(SystemManager.song.bufferSize(),
				SystemManager.song.sampleRate());
		
		SystemManager.fftOscil = new FFT(SystemManager.out.bufferSize(),
				SystemManager.out.sampleRate());

		SystemManager.fftIn = new FFT(SystemManager.in.bufferSize(), SystemManager.in.sampleRate());
		
		//Initialize the oscillator with all the arguments needed, which is frequency, amplitude, output, and wave type
		osc = new dk.aau.medialogy.y2013.p4.Oscillator(200f, 0.5f, SystemManager.out, waveType);
		
		//Initialize starter
		starter = new Multiplier();
		
		//Initialize the effectslist and store starter in it
		effectsList = new ArrayList<UGen>();
		effectsList.add(starter);

	}

	//Function which is constantly looped until the program is exited
	public void draw()
	{
		//check if anything has changed
		if(oscilFreq != SystemManager.cp5.getController("OscilFrequency").getValue())
		{
			freqChanged = true;
			oscilFreq = SystemManager.cp5.getController("OscilFrequency").getValue();
		}
		if(effectsList.size() != lastSize)
		{
			effectsChanged = true;
			lastSize = effectsList.size();
		}
		
		//Perform actions if anything is different. This is used if the oscillator and the effects is actually used, and would be the system used if the app was developed further
		if(effectsChanged || freqChanged)
		{
//			unpatchEffects();
			osc.changeFrequency(SystemManager.cp5.getController("OscilFrequency").getValue());		
//			patchEffects();
			effectsChanged = freqChanged = false;
		}
		
		//Set background colour
		background(39, 35, 33);
		
		//Run FFT on the different outputs
		SystemManager.fftSong.forward(SystemManager.song.mix);

		SystemManager.fftOscil.forward(SystemManager.out.mix);
		
		SystemManager.fftIn.forward(SystemManager.in.mix);
		
		//Would be used in the full program
		//Button.draw();

		// draw stuff
		fill(39, 35, 32);
		noStroke();
		rect(0, (SystemManager.HEIGHT / 2)
				- (SystemManager.MIDT_BOX_HEIGHT / 2),
				SystemManager.WINDOW_WIDTH, (SystemManager.HEIGHT / 2)
						+ (SystemManager.MIDT_BOX_HEIGHT / 2));

		fill(0);
		noStroke();
		rect(0, SystemManager.HEIGHT - SystemManager.WINDOW_HEIGHT,
				SystemManager.WINDOW_WIDTH, SystemManager.WINDOW_HEIGHT);

		
		// Draw frequency domain
		if (showVisual == true)
		{
		for (int i = 0; i <= SystemManager.fftOscil.specSize(); i++)
		{

			// 1 - song
			if (playSong == true)
			{
				float z1 = map(i, 0, SystemManager.fftSong.specSize(), 0, SystemManager.WINDOW_WIDTH);
				//float z2 = map(SystemManager.fftSong.getBand(i), 0, 1, SystemManager.HEIGHT, SystemManager.HEIGHT - SystemManager.WINDOW_HEIGHT);
				stroke(255, 0, 0);
				line(z1, SystemManager.WINDOW_HEIGHT + SystemManager.MIDT_BOX_HEIGHT + SystemManager.WINDOW_HEIGHT, 
						z1, SystemManager.WINDOW_HEIGHT + SystemManager.MIDT_BOX_HEIGHT + SystemManager.WINDOW_HEIGHT
						- SystemManager.fftSong.getBand(i)*4);
			}
			else if (UseMic == true) 
			{
			// 2 - out
				float temp_val;
				if(SystemManager.WINDOW_HEIGHT + SystemManager.MIDT_BOX_HEIGHT + SystemManager.WINDOW_HEIGHT
						- SystemManager.fftIn.getBand(i)*4 >= SystemManager.HEIGHT)
				{
				temp_val = SystemManager.WINDOW_HEIGHT;
				}
				else
				{
					temp_val = SystemManager.WINDOW_HEIGHT + SystemManager.MIDT_BOX_HEIGHT + SystemManager.WINDOW_HEIGHT
							- SystemManager.fftIn.getBand(i)*4;
				}
				float y1 = map(i, 0, SystemManager.fftIn.specSize(), 0, SystemManager.WINDOW_WIDTH);
				stroke(23, 254, 255);
				line(y1, SystemManager.WINDOW_HEIGHT + SystemManager.MIDT_BOX_HEIGHT + SystemManager.WINDOW_HEIGHT, 
						y1, temp_val);
			}
			else
			{
			
			// 3 - in
				float x1 = map(i, 0, SystemManager.fftOscil.specSize(), 0, SystemManager.WINDOW_WIDTH);
				stroke(0, 255, 0);
				line(x1, SystemManager.WINDOW_HEIGHT + SystemManager.MIDT_BOX_HEIGHT + SystemManager.WINDOW_HEIGHT, 
						x1, SystemManager.WINDOW_HEIGHT + SystemManager.MIDT_BOX_HEIGHT + SystemManager.WINDOW_HEIGHT
						- SystemManager.fftOscil.getBand(i));
			}
		}
		}

		noStroke();
		fill(0);
		rect(0, 0, SystemManager.WINDOW_WIDTH, SystemManager.WINDOW_HEIGHT);
		stroke(23, 254, 255);

		float temp_out[] = SystemManager.out.left.toArray();
		float temp_in[] = SystemManager.in.left.toArray();
		float temp_song[] = SystemManager.song.left.toArray();
		
		// DRAW TIME DOMAIN
		if (showVisual == true)
		{
		for (int i = 0; i < SystemManager.WINDOW_WIDTH - 1; i++)
		{
			float x1 = map(i, 0, SystemManager.out.bufferSize(), 0, SystemManager.WINDOW_WIDTH + 160);
		    float x2 = map(i+1, 0, SystemManager.out.bufferSize(), 0, SystemManager.WINDOW_WIDTH + 160);
		    float y1 = map(i, 0, SystemManager.in.bufferSize(), 0, SystemManager.WINDOW_WIDTH + 160);
		    float y2 = map(i+1, 0, SystemManager.in.bufferSize(), 0, SystemManager.WINDOW_WIDTH + 160);
		    float z1 = map(i, 0, SystemManager.song.bufferSize(), 0, SystemManager.WINDOW_WIDTH + 160);
		    float z2 = map(i+1, 0, SystemManager.song.bufferSize(), 0, SystemManager.WINDOW_WIDTH + 160);
			if (playSong == true)
			{
		    line(z1,
					SystemManager.WINDOW_HEIGHT / 2
							+ temp_song[i]
							* (SystemManager.WINDOW_HEIGHT - 50),
					z2,
					SystemManager.WINDOW_HEIGHT / 2
							+ temp_song[i+1]
							* (SystemManager.WINDOW_HEIGHT - 50));
			}
			else if (UseMic == true)
			{
				line(y1,
						SystemManager.WINDOW_HEIGHT / 2
								+ temp_in[i]
								* (SystemManager.WINDOW_HEIGHT - 50),
						y2,
						SystemManager.WINDOW_HEIGHT / 2
								+ temp_in[i+1]
								* (SystemManager.WINDOW_HEIGHT - 50));
			}
			else
			{
				line(x1,
						SystemManager.WINDOW_HEIGHT / 2
								+ temp_out[i]
								* (SystemManager.WINDOW_HEIGHT - 50),
						x2,
						SystemManager.WINDOW_HEIGHT / 2
								+ temp_out[i+1]
								* (SystemManager.WINDOW_HEIGHT - 50));
			}
			line(i, SystemManager.WINDOW_HEIGHT / 2, i + 1,
					SystemManager.WINDOW_HEIGHT / 2);
		}
		}

		noStroke();
		fill(0, 0, 0);
		rect(SystemManager.ADDED_BAR, SystemManager.BUTTON_SIZE,
				SystemManager.WIDTH, SystemManager.HEIGHT);

	}
	
	//Apply effects. Basically goes through every single effect and patches it where appropriate. Would have to be reworked to work with all kinds of effects
	public void patchEffects()
	{
		if (effectsList.size() > 0)
		{
			for (int i = 0; i < effectsList.size(); i++)
			{
				if(i == 0)
				{
					osc.patch(effectsList.get(i));
				}
				else
				{
					effectsList.get(i-1).patch(effectsList.get(i));
				}
			}
			
			effectsList.get(effectsList.size() - 1).patch(SystemManager.out);
		}
	}
	
	//Remove effects. Basically goes through every single effect and unpatches it where appropriate. Would have to be reworked to work with all kinds of effects
	public void unpatchEffects()
	{
		if(effectsList.size() > 0)
		{
			effectsList.get(effectsList.size()-1).unpatch(SystemManager.out);
			
			for (int i = effectsList.size() - 1; i >= 0; i--)
			{
				if(i == 0)
				{
					try {
						osc.unpatch(effectsList.get(i));
					} catch (NullPointerException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				else
				{
					try {
						effectsList.get(i-1).unpatch(effectsList.get(i));
					} catch (NullPointerException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}

	//Buttons left side buttons
	public void chorus_Button(int theValue) 
	{
		hideAll();
		SystemManager.cp5.getController("chorus_Button").setImage(img1d);
		SystemManager.cp5.getController("Chorus1").show();
		SystemManager.cp5.getController("Chorus2").show();
		SystemManager.cp5.getController("Chorus3").show();
		SystemManager.cp5.getController("Chorus4").show();
		SystemManager.cp5.getController("choruslabel").show();
	}
	
	public void distortion_Button(int theValue) 
	{
		hideAll();
		SystemManager.cp5.getController("distortion_Button").setImage(img2d);
		SystemManager.cp5.getController("distortion1").show();
		SystemManager.cp5.getController("distortion2").show();
		SystemManager.cp5.getController("distortion3").show();
		SystemManager.cp5.getController("distortionlabel").show();
	}
	
	public void echo_Button(int theValue) 
	{
		hideAll();
		SystemManager.cp5.getController("echo_Button").setImage(img3d);
		SystemManager.cp5.getController("echo1").show();
		SystemManager.cp5.getController("echo2").show();
		SystemManager.cp5.getController("echo3").show();
		SystemManager.cp5.getController("echo4").show();
		SystemManager.cp5.getController("echolabel").show();
	}
	
//	public void radioButton(int theValue)
//	{
//		if (theValue == 1)
//		{
//			//resetAll();
//			SystemManager.cp5.getController("Music").setValue(0);
//			
//			
//		}
//		else 
//		{
//			SystemManager.cp5.getController("SineWave").setValue(0);
//		}
//	}
	
	public void equilizer_Button(int theValue) 
	{
		hideAll();
		SystemManager.cp5.getController("equilizer_Button").setImage(img4d);
		SystemManager.cp5.getController("equilizer1").show();
		SystemManager.cp5.getController("equilizer2").show();
		SystemManager.cp5.getController("equilizer3").show();
		SystemManager.cp5.getController("equilizer4").show();
		SystemManager.cp5.getController("equilizerlabel").show();
	}
	
	public void phasor_Button(int theValue) 
	{
		hideAll();
		SystemManager.cp5.getController("phasor_Button").setImage(img5d);
		SystemManager.cp5.getController("phasor1").show();
		SystemManager.cp5.getController("phasor2").show();
		SystemManager.cp5.getController("phasor3").show();
		SystemManager.cp5.getController("phasor4").show();
		SystemManager.cp5.getController("phasor5").show();
		SystemManager.cp5.getController("phasorlabel").show();
	}
	
	public void tremolo_Button(int theValue) 
	{
		hideAll();
		SystemManager.cp5.getController("tremolo_Button").setImage(img6d);
		SystemManager.cp5.getController("tremolo1").show();
		SystemManager.cp5.getController("tremololabel").show();
	}
	
	public void vibrato_Button(int theValue) 
	{
		hideAll();
		SystemManager.cp5.getController("vibrato_Button").setImage(img7d);
		SystemManager.cp5.getController("vibrato1").show();
		SystemManager.cp5.getController("vibratolabel").show();
	}
	
	public void wahwah_Button(int theValue) 
	{
		hideAll();
		SystemManager.cp5.getController("wahwah_Button").setImage(img8d);
		SystemManager.cp5.getController("wahwah1").show();
		SystemManager.cp5.getController("wahwah2").show();
		SystemManager.cp5.getController("wahwah3").show();
		SystemManager.cp5.getController("wahwahlabel").show();
	}

	//Stop the program
	public void stop()
	{
		SystemManager.song.close();
		SystemManager.minim.stop();
		SystemManager.out.close();

		super.stop();
	}
}
